﻿using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Collections.Generic;
using System.Text;
using Mud.Client.WPF.Graphics;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using Mud.Common.Physical;

namespace Mud.Client.WPF
{
    public partial class AreaView
    {


        private ProjectionCamera _camera;

        /// <summary>
        /// Client.
        /// </summary>
        public Client Client
        {
            get { return _client; }
        }
        private Client _client;


        /// <summary>
        /// Creatures.
        /// </summary>
        public IDictionary<Creature, CreatureModel> CreatureModels
        {
            get { return _creatures; }
        }
        private Dictionary<Creature, CreatureModel> _creatures = new Dictionary<Creature, CreatureModel>();


        /// <summary>
        /// Land square models.
        /// </summary>
        public LandSquareModel[,] LandModels
        {
            get { return _areaSquares; }
        }
        private LandSquareModel[,] _areaSquares;


        public CreatureModel PlayerModel
        {
            get { return _playerModel; }
        }
        private CreatureModel _playerModel;



        /// <summary>
        /// Initializes a new instance of the <see cref="AreaView"/> class.
        /// </summary>
        /// <param name="client">The client.</param>
        public AreaView(Client client)
        {
            this.InitializeComponent();

            // Insert code required on object creation below this point.
            if (client == null)
                throw new ArgumentNullException("client");
            _client = client;
            _client.ClientNewPlayerCharacter += ClientNewCharacter;
            _client.ClientAreaChanged += ClientAreaChanged;
            _client.ClientStartedPlaying += ClientStartedPlaying;
            _client.ClientEndedPlaying += ClientEndedPlaying;
            AreaViewport.MouseDown += AreaViewport_MouseDown;
            this.Background = new SolidColorBrush(Colors.WhiteSmoke);
        }

        #region Client event handling

        /// <summary>
        /// Called when the client starts to control a character.
        /// </summary>
        private void ClientNewCharacter(Creature oldCharacter, Creature newCharacter)
        {
            if (oldCharacter != null)
                oldCharacter.GameObjectChanged -= ClientPlayerCharacterChanged;
            if (newCharacter != null)
                newCharacter.GameObjectChanged += ClientPlayerCharacterChanged;

            if (_playerModel != null)
                _playerModel.RemoveFromScene();

            _playerModel = new CreatureModel(this.AreaViewport, newCharacter);
            _playerModel.AddToScene();
            RepositionCamera();
        }

        /// <summary>
        /// Called when the area is replaced with new area.
        /// </summary>
        private void ClientAreaChanged(Area oldArea, Area newArea)
        {
            if (oldArea != null)
            {
                oldArea.AreaChanged -= AreaChanged;
                oldArea.CreatureAdded -= AreaCreatureAdded;
                oldArea.CreatureRemoved -= AreaCreatureRemoved;
                oldArea.ObjectAdded -= AreaObjectAdded;
                oldArea.ObjectRemoved -= AreaObjectRemoved;

            }
            if (newArea != null)
            {
                newArea.AreaChanged += AreaChanged;
                newArea.CreatureAdded += AreaCreatureAdded;
                newArea.CreatureRemoved += AreaCreatureRemoved;
                newArea.ObjectAdded += AreaObjectAdded;
                newArea.ObjectRemoved += AreaObjectRemoved;
            }
            CreateCurrentClientArea();
        }

        /// <summary>
        /// Called when the clients player character characteristics change.
        /// </summary>
        private void ClientPlayerCharacterChanged(GameObject o)
        {
            RepositionCamera();
        }

        /// <summary>
        /// Called when the client starts to play.
        /// </summary>
        private void ClientStartedPlaying()
        {
        }

        /// <summary>
        /// Called when the client ends play.
        /// </summary>
        private void ClientEndedPlaying()
        {
        }

        /// <summary>
        /// Called when the current area changes.
        /// </summary>
        private void AreaChanged(Area a, Location position)
        {
            if (_areaSquares[position.X, position.Y] != null)
                _areaSquares[position.X, position.Y].RemoveFromScene();
            LandSquareModel square = new LandSquareModel(this.AreaViewport, a.Land[position.X, position.Y]);
            square.AddToScene();
            _areaSquares[position.X, position.Y] = square;
        }

        /// <summary>
        /// Called when a creature is added to the area.
        /// </summary>
        private void AreaCreatureAdded(Area a, Creature c)
        {
            if (c == _client.PlayerCharacter)
                return;
            CreatureModel model = new CreatureModel(this.AreaViewport, c);
            _creatures.Add(c, model);
            model.AddToScene();
        }

        /// <summary>
        /// Called when a creature is removed from the area.
        /// </summary>
        private void AreaCreatureRemoved(Area a, Creature c)
        {
            if (c == _client.PlayerCharacter)
                return;
            CreatureModel model = _creatures[c];
            model.RemoveFromScene();
            _creatures.Remove(c);
        }


        /// <summary>
        /// Called when a object is added to the area.
        /// </summary>
        private void AreaObjectAdded(Area a, Object o)
        {
        }

        /// <summary>
        /// Called when a object is removed from the area.
        /// </summary>
        private void AreaObjectRemoved(Area a, Object o)
        {
        }

        /// <summary>
        /// Called when an object changes (but not the player character).
        /// </summary>
        private void AreaObjectChanged(GameObject o)
        {
        }

        #endregion

        #region Area view events

        /// <summary>
        /// Handles the MouseDown event of the AreaViewport control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        void AreaViewport_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Client.Tick();
            Point location = e.GetPosition(AreaViewport);
            HitTestResult result = VisualTreeHelper.HitTest(AreaViewport, location);
            if (result == null)
                return;

            ModelVisual3D model = result.VisualHit as ModelVisual3D;
            if (model == null)
                return;
            foreach (LandSquareModel landModel in _areaSquares)
            {
                if (model == landModel.ModelVisual3D)
                {
                    Client.RequestMoveCharacter(new Location(landModel.LandSquare.X, landModel.LandSquare.Y), Mud.Common.Physical.MovementType.Running);
                    break;
                }
            }
        }


        #endregion

        #region Helper methods


        /// <summary>
        /// Creates the current client area.
        /// </summary>
        private void CreateCurrentClientArea()
        {
            Creature pc = _client.PlayerCharacter;
            Area area = _client.PlayerArea;

            this.AreaViewport.Children.Clear();

            _playerModel = new CreatureModel(this.AreaViewport, pc);
            _playerModel.AddToScene();

            _camera = new PerspectiveCamera();
            RepositionCamera();
            this.AreaViewport.Camera = _camera;
            ModelVisual3D lightModel = new ModelVisual3D();
            lightModel.Content = new DirectionalLight(Colors.White, new Vector3D(1, 1, -1));
            this.AreaViewport.Children.Add(lightModel);
            _areaSquares = new LandSquareModel[area.Size.Width, area.Size.Height];
        }



        /// <summary>
        /// Repositions the camera.
        /// </summary>
        private void RepositionCamera()
        {
            if (_camera != null)
            {
                Creature pc = _client.PlayerCharacter;
                _camera.Position = new Point3D(_playerModel.Creature.Location.X - 10, _playerModel.Creature.Location.Y - 10, 18);
                _camera.UpDirection = new Vector3D(0, 0, 1);
                LookAt(new Point3D(_playerModel.Creature.Location.X, _playerModel.Creature.Location.Y, 0));
            }

        }

        /// <summary>
        /// Looks at point.
        /// </summary>
        /// <param name="lookAtPoint">Point to look at.</param>
        private void LookAt(Point3D lookAtPoint)
        {
            _camera.LookDirection = lookAtPoint - _camera.Position;
        }

        #endregion


    }
}